# -*- coding: utf-8 -*-

# mayavi嵌入pyqt程序中，必须先引用并设置环境变量
import os
os.environ['ETS_TOOLKIT'] = 'qt4'
os.environ['QT_API'] = 'pyqt'
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"     #自适应不同分辨率
import sip
sip.setapi('QString', 2)


import sys
from time import strftime, localtime, time, sleep
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QMessageBox, QFileDialog, QSizePolicy
from PyQt5 import QtCore, QtGui, QtWidgets

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure


from MainWin import *
from proj_form import *
from imaging_form import *
from reconstruct_form import *
from measure_form import *
#from option_form import *
from help_form import *
from proj_dialog import *
from camera_form import *

from photometric_stereo import *
from surface_args import *

import numpy as np
import cv2

from traits.api import HasTraits, Instance, on_trait_change
from traitsui.api import View, Item
from mayavi.core.ui.api import MayaviScene, MlabSceneModel, SceneEditor
from mayavi import mlab
from tvtk.api import tvtk

from serial import Serial as Ser  # 控制光源电路 pip install pyserial
#import win32api    #调用外部程序需应用的包

np.seterr(divide='ignore', invalid='ignore')


def test():
    print('test')
class MyMainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):    
        super(MyMainWindow, self).__init__(parent)
        self.setupUi(self)

        self.progressBar.hide()
        
        #生成项目、成像、重构、测量、设置、帮助窗口类实例
        self.proj_child = MyProjChildForm()
        self.imaging_child = MyImagingChildForm()
        self.reconstruct_child = MyReconstructChildForm()
        self.measure_child = MyMeasureChildForm()
        #self.option_child = MyOptionChildForm()
        self.help_child = MyHelpChildForm()

        self.gridLayout_right = QtWidgets.QGridLayout(self.right_widget)
        self.gridLayout_right.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_right.setSpacing(0)
        self.gridLayout_right.setObjectName("gridLayout_right")
        
        #将所有子窗口添加到主窗口并隐藏所有
        self.gridLayout_right.addWidget(self.proj_child, 0, 0, 1, 1)
        self.gridLayout_right.addWidget(self.imaging_child, 0, 0, 1, 1)
        self.gridLayout_right.addWidget(self.reconstruct_child, 0, 0, 1, 1)
        self.gridLayout_right.addWidget(self.measure_child, 0, 0, 1, 1)
        self.gridLayout_right.addWidget(self.help_child, 0, 0, 1, 1)
        self.all_child_hide()
        
        #初始化显示项目窗口
        self.proj_child.show()
        
        #关联按钮调用子窗口事件
        self.proj_button.clicked.connect(self.proj_child_show)
        self.imaging_button.clicked.connect(self.imaging_child_show)
        self.reconstruct_button.clicked.connect(self.reconstruct_child_show)
        self.measure_button.clicked.connect(self.measure_child_show)
        #self.option_button.clicked.connect(self.option_child_show)
        self.help_button.clicked.connect(self.help_child_show)
    
    #隐藏所有子窗口
    def all_child_hide(self):
        self.proj_child.hide()
        self.imaging_child.hide()
        self.reconstruct_child.hide()
        self.measure_child.hide()
        #self.option_child.hide()
        self.help_child.hide()

    #调用项目、成像、重构、测量、设置、帮助子窗口
    def proj_child_show(self):
        self.all_child_hide()
        #self.gridLayout_right.addWidget(self.proj_child, 0, 0, 1, 1)
        self.proj_child.show()

    def imaging_child_show(self):
        self.all_child_hide()
        #self.gridLayout_right.addWidget(self.imaging_child, 0, 0, 1, 1)
        self.imaging_child.show()

    def reconstruct_child_show(self):
        self.all_child_hide()
        #self.gridLayout_right.addWidget(self.reconstruct_child, 0, 0, 1, 1)
        self.reconstruct_child.show()

    def measure_child_show(self):
        self.all_child_hide()
        #self.gridLayout_right.addWidget(self.measure_child, 0, 0, 1, 1)
        self.measure_child.show()
    '''
    def option_child_show(self):
        self.all_child_hide()
        self.gridLayout_right.addWidget(self.option_child, 0, 0, 1, 1)
        self.option_child.show()
    '''
    def help_child_show(self):
        self.all_child_hide()
        #self.gridLayout_right.addWidget(self.help_child, 0, 0, 1, 1)
        self.help_child.show()



#继承项目窗口类
class MyProjChildForm(QWidget, Ui_proj_form):
    def __init__(self, parent=None):    
        super(MyProjChildForm, self).__init__(parent)
        self.setupUi(self)

        self.proj_path = os.path.abspath('proj')
        self.proj_num_path = os.path.abspath('proj')
        

        self.pushButton_proj_new.clicked.connect(self.show_proj_dialog)
        self.pushButton_proj_open.clicked.connect(self.open_proj)
        self.pushButton_proj_exit.clicked.connect(QtCore.QCoreApplication.instance().quit)

    #输入实验信息
    def show_proj_dialog(self):
        dialog = QtWidgets.QDialog()
        
        dialog_ui = Ui_Dialog()
        dialog_ui.setupUi(dialog)
        dialog_ui.proj_time.setText(strftime('%Y-%m-%d %H:%M', localtime(time())))
        rsp = dialog.exec_()
        if rsp == QtWidgets.QDialog.Accepted:
            name = dialog_ui.proj_name.text()
            num = dialog_ui.proj_num.text()
            user = dialog_ui.proj_user.text()
            date = dialog_ui.proj_time.text()
            if not (name and num and user):
                msg = QMessageBox.warning(self, "警告", "请重新输入完整的实验信息",
                                        QMessageBox.Ok, QMessageBox.Ok)
            else:
                #创建文件夹及保存实验信息
                self.proj_num_path = os.path.join(self.proj_path, num)
                if os.path.exists(self.proj_num_path):
                    msg = QMessageBox.warning(self, "警告", "实验编号已存在，请重新输入",
                                        QMessageBox.Ok, QMessageBox.Ok)
                else:
                    os.makedirs(os.path.join(self.proj_num_path, 'images'))
                    info = np.array([name, num, user, date, self.proj_num_path])
                    info_path = os.path.join(self.proj_num_path, num.lower()+'_info.npy')
                    np.save(info_path, info)
                    
    #打开与查看已有实验
    def open_proj(self):
        self.proj_num_path = QFileDialog.getExistingDirectory(self, "选取文件夹", './proj')
        #若取消文件打开，直接结束
        if not self.proj_num_path:
            return
        
        num = os.path.split(self.proj_num_path)[1]  #获取实验编号
        if num == 'proj':
            msg = QMessageBox.warning(self, "警告", "请选择正确文件路径",
                                        QMessageBox.Ok, QMessageBox.Ok)
            return
        #对选择的实验文件夹进行判断
        info_exist = os.path.exists(os.path.join(self.proj_num_path, num.lower() + '_info.npy'))
        images_path_exist = os.path.exists(os.path.join(self.proj_num_path, 'images'))
        img_exist = True
        if images_path_exist:
            for i in range(0, 9):
                img_path = os.path.join(self.proj_num_path, 'images', str(i) + '.jpg')
                if not os.path.exists(img_path):
                    img_exist = False
        texture_exist = os.path.exists(os.path.join(self.proj_num_path, 'texture.jpg'))
        display_exist = os.path.exists(os.path.join(self.proj_num_path, 'display.jpg'))
        z_exist = os.path.exists(os.path.join(self.proj_num_path, 'Z.npy'))
        
        #实验信息完整
        if (info_exist and images_path_exist and img_exist and texture_exist and display_exist and z_exist):
            info = np.load(os.path.join(self.proj_num_path, num.lower() + '_info.npy'))
            msg_text = "实验信息读取成功，点击重构-三维重建进行查看\n分析对象：%s\n分析编号：%s\n分析人员：%s\n分析时间：%s" % (info[0], info[1], info[2], info[3])
            msg = QMessageBox.warning(self, "提示", msg_text,
                                        QMessageBox.Ok, QMessageBox.Ok)
        #实验文件夹缺少三维数据但图像在，进行重新计算即可
        elif (info_exist and images_path_exist and img_exist):
            msg = QMessageBox.warning(self, "提示", "实验信息读取成功，点击重构-法向求解进行重构",
                                        QMessageBox.Ok, QMessageBox.Ok)
        #实验文件夹缺少图像
        elif (info_exist):
            msg = QMessageBox.warning(self, "警告", "实验图像缺失，请新建项目重新分析",
                                        QMessageBox.Ok, QMessageBox.Ok)
        else:
            msg = QMessageBox.warning(self, "警告", "实验信息缺失，请新建项目重新分析",
                                        QMessageBox.Ok, QMessageBox.Ok)



#继承成像窗口类
class MyImagingChildForm(QWidget, Ui_imaging_form):
    def __init__(self, parent=None):    
        super(MyImagingChildForm, self).__init__(parent)
        self.setupUi(self)

        self.file_path = os.path.split(os.path.realpath(__file__))[0].replace('\\','/')   #获取绝对路径
        
        self.camera_child = MyCameraChildForm()

        self.groupBox_imaging.hide()  #初始化隐藏
        self.gridLayout_imaging_right.addWidget(self.camera_child, 0, 0, 0, 0)

        #关联事件
        self.pushButton_imaging_shoot.clicked.connect(self.call_supereyes_exe)
        self.pushButton_imaging_show.clicked.connect(self.pic_show)
        self.pushButton_imaging_process.clicked.connect(self.pic_compression)

    #调用拍摄程序
    def call_supereyes_exe(self):
        self.groupBox_imaging.hide()
        self.camera_child.show()
        
        #调用相机拍摄软件
        '''
        supereyes_exe_path=self.file_path + "/supereyes/supereyes.exe"
        os.system(supereyes_exe_path)
        '''

    def pic_show(self):
        self.camera_child.hide()
        self.groupBox_imaging.show()

        self.pic_rename()
        image_path = myWin.proj_child.proj_num_path + os.sep + 'images' + os.sep
        self.toolButton_imaging_0.setIcon(QtGui.QIcon(image_path + '0.jpg'))
        self.toolButton_imaging_1.setIcon(QtGui.QIcon(image_path + '1.jpg'))
        self.toolButton_imaging_2.setIcon(QtGui.QIcon(image_path + '2.jpg'))
        self.toolButton_imaging_3.setIcon(QtGui.QIcon(image_path + '3.jpg'))
        self.toolButton_imaging_4.setIcon(QtGui.QIcon(image_path + '4.jpg'))
        self.toolButton_imaging_5.setIcon(QtGui.QIcon(image_path + '5.jpg'))
        self.toolButton_imaging_6.setIcon(QtGui.QIcon(image_path + '6.jpg'))
        self.toolButton_imaging_7.setIcon(QtGui.QIcon(image_path + '7.jpg'))
        self.toolButton_imaging_8.setIcon(QtGui.QIcon(image_path + '8.jpg'))

    #磨损图像重命名（编号）
    def pic_rename(self):
        #pic_file_path = self.file_path + "/images/"  #表示需要命名处理的文件夹
        pic_file_path = myWin.proj_child.proj_num_path + os.sep + 'images' + os.sep
        
        filelist = os.listdir(pic_file_path) #获取文件路径
        pic_num = len(filelist) #获取文件长度（个数）
        i = 0  #表示文件的命名是从0开始的
        for item in filelist:
            if item.endswith('.jpg'):  #初始的图片的格式为jpg格式的（或者源文件是png格式及其他格式，后面的转换格式就可以调整为自己需要的格式即可）
                src = os.path.join(os.path.abspath(pic_file_path), item)
                dst = os.path.join(os.path.abspath(pic_file_path), str(i) + '.jpg')#处理后的格式也为jpg格式的，当然这里可以改成png格式
                try:
                    os.rename(src, dst)
                    i = i + 1
                except:
                    continue

    #磨损图像预处理（压缩图片）
    def pic_compression(self):
        self.camera_child.hide()
        self.groupBox_imaging.show()
        
        #pic_file_path_2 = self.file_path + "/images/"
        pic_file_path_2 = myWin.proj_child.proj_num_path + os.sep + 'images' + os.sep
        
        pic_list = os.listdir(pic_file_path_2)
        pic_num_2 = len(pic_list)
        for i in range(pic_num_2):
            pic_path = pic_file_path_2 + str(i) + '.jpg'
            image = cv2.imread(pic_path)
            res = cv2.resize(image, (800, 600), interpolation=cv2.INTER_AREA)
            cv2.imwrite(pic_path, res)


#继承重构窗口类
class MyReconstructChildForm(QWidget, Ui_reconstruct_form):
    def __init__(self, parent=None):    
        super(MyReconstructChildForm, self).__init__(parent)
        self.setupUi(self)

        self.timer = QtCore.QBasicTimer()  #初始化一个时钟，用于控制进度条进度
        self.step = 0   #进度条初始值
        
        #mayavi绘图窗口嵌入
        self.mayavi_child = MayaviQWidget()
        self.gridLayout_mayavi = QtWidgets.QGridLayout(self.widget_reconstruct_right)
        self.gridLayout_mayavi.setObjectName("gridLayout_mayavi")
        self.gridLayout_mayavi.addWidget(self.mayavi_child, 0, 0, 0, 0)
        
        self.pushButton_reconstruct_calc.clicked.connect(self.pushButton_reconstruct_calc_click)
        self.pushButton_reconstruct_surface.clicked.connect(self.surface_show)
        self.pushButton_reconstruct_height.clicked.connect(self.height_show)

    def pushButton_reconstruct_calc_click(self):
        myWin.progressBar.show()
        self.pushButton_reconstruct_calc.setEnabled(False)
        self.timer.start(700, self)     #启动QBsicTimer，每800ms调用一次回调函数
        #初始化进程
        self.runthread_reconstruct = Reconthread()
        self.runthread_reconstruct.reconstruct_signal.connect(self.reconstruct_callback)    # 连接重构线程的信号
        self.runthread_reconstruct.start()

    def surface_show(self):
        self.mayavi_child.visualization.plot_surface()

    def height_show(self):
        self.mayavi_child.visualization.plot_height()

    # 接收到重构线程信号后回调函数
    def reconstruct_callback(self, signal):
        if signal:
            self.step = 100
            myWin.progressBar.setValue(self.step)
            #self.timer.stop()
            QMessageBox.information(self, "提示", "重构计算完成")
            self.pushButton_reconstruct_calc.setEnabled(True)
            #self.step = 0
            #myWin.progressBar.hide()
            return
        else:
            myWin.progressBar.hide()
            QMessageBox.Warning(self, "警告", "出现错误")
            self.pushButton_reconstruct_calc.setEnabled(True)
            return

    # 时钟回调函数
    def timerEvent(self, *args, **kwargs):
        myWin.progressBar.setValue(self.step)
        if self.step >= 100:
            self.timer.stop()
            self.step = 0
            myWin.progressBar.setValue(self.step)
            myWin.progressBar.hide()
            return
        if self.step < 99:
            self.step += 1
            


        


#继承测量窗口类
class MyMeasureChildForm(QWidget, Ui_measure_form):
    def __init__(self, parent=None):    
        super(MyMeasureChildForm, self).__init__(parent)
        self.setupUi(self)

        self.calc_timer = QtCore.QBasicTimer()      #初始化时钟，用于进度条控制
        self.step = 0   #进度条初始值
        
        
        #第四步：定义MyFigure类的实列
        ra_fig_info = { "xlabel": "水平方向/像素",
                        "ylabel": "高度/像素",
                        "title": "二维轮廓曲线"}
        self.ra_fig = MyFigure(width=10, height=2, **ra_fig_info)
        self.ra_fig.fig.subplots_adjust(bottom=0.22)
        
        #self.ra_fig_ntb = NavigationToolbar(self.ra_fig, self)  #matplotlib工具栏

        smrc_fig_info = {   "xlabel": "支承率Smr",
                            "ylabel": "高度c",
                            "title": "支承率Smr-高度c曲线图"}
        self.smrc_fig = MyFigure(**smrc_fig_info)
        smrvvv_fig_info = { "xlabel": "支承率Smr",
                            "ylabel": "空体积Vvv",
                            "title": "支承率Smr-空体积Vvv曲线图"}
        self.smrvvv_fig = MyFigure(**smrvvv_fig_info)
        smrvmp_fig_info = { "xlabel": "支承率Smr",
                            "ylabel": "峰体积Vmp",
                            "title": "支承率Smr-峰体积Vmp曲线图"}
        self.smrvmp_fig = MyFigure(**smrvmp_fig_info)
        

        #第五步：在Gui的某控件中创建一个布局，用于添加MyFigure类的实例后的其它部件
        self.gridlayout_plot_ra = QtWidgets.QGridLayout(self.widget_measure_center)
        self.gridlayout_plot_ra.setContentsMargins(0, 0, 0, 0)

        self.gridlayout_plot_ra.setObjectName("gridlayout_plot_ra")
        self.gridlayout_plot_ra.addWidget(self.ra_fig)
        
        self.gridLayout_tab3d_1 = QtWidgets.QGridLayout(self.tab_smrc)
        self.gridLayout_tab3d_1.setObjectName("gridLayout_tab3d_1")
        self.gridLayout_tab3d_1.addWidget(self.smrc_fig)

        self.gridLayout_tab3d_2 = QtWidgets.QGridLayout(self.tab_smrvvv)
        self.gridLayout_tab3d_2.setObjectName("gridLayout_tab3d_2")
        self.gridLayout_tab3d_2.addWidget(self.smrvvv_fig)

        self.gridLayout_tab3d_3 = QtWidgets.QGridLayout(self.tab_smrvmp)
        self.gridLayout_tab3d_3.setObjectName("gridLayout_tab3d_3")
        self.gridLayout_tab3d_3.addWidget(self.smrvmp_fig)


        #连接事件

        #self.verticalSlider.valueChanged.connect()
        self.pushButton_2d.clicked.connect(self.button_2d_clicked)
        self.pushButton_2d_export.clicked.connect(self.export_2d_data)
        self.pushButton_3d.clicked.connect(self.calc_3d_args)
        self.pushButton_3d_export.clicked.connect(self.export_3d_data)
        



    
    #定义绘粗糙度曲线函数
    def plot_ra(self):
        row = self.verticalSlider.value()
        x = np.arange(0, 600) * 1.333333
        height = np.load(myWin.proj_child.proj_num_path + os.sep + 'Z.npy')
        y = height[599 - row]

        #self.ra_fig.fig.subplots_adjust(bottom=0.22)
        self.ra_fig.axes.plot(x, y)
        self.ra_fig.axes.set_ylim(np.min(y)*3,np.max(y)*3)
        self.ra_fig.axes.set_xlabel("水平方向/微米", fontproperties="SimHei")
        self.ra_fig.axes.set_ylabel("高度/微米", fontproperties="SimHei")
        self.ra_fig.axes.set_title("二维轮廓曲线", fontproperties="SimHei")
        
    def calc_2d_args(self):
        '''计算并显示粗糙度参数'''
        ##
        self.label_image.setPixmap(QtGui.QPixmap(myWin.proj_child.proj_num_path + os.sep + "display.jpg"))

        z_data = np.load(myWin.proj_child.proj_num_path + os.sep + 'Z.npy')
        x_coord = 599 - self.verticalSlider.value()

        surface_args_2d = Extract_2d(z_data, x_coord)
        roughness = surface_args_2d.calc_2d_args()
        
        self.label_rp.setText(roughness[0])
        self.label_rv.setText(roughness[1])
        self.label_rz.setText(roughness[2])
        self.label_rc.setText(roughness[3])
        self.label_ra.setText(roughness[4])
        self.label_rq.setText(roughness[5])
        self.label_rsm.setText(roughness[6])
        self.label_rmrc.setText(roughness[7])
        self.label_rsk.setText(roughness[8])
        self.label_rku.setText(roughness[9])        
        self.label_rdq.setText(roughness[10])
        self.label_rdc.setText(roughness[11])


    def button_2d_clicked(self):
        self.ra_fig.axes.cla()
        self.plot_ra()
        self.ra_fig.draw()

        self.calc_2d_args()

    def export_2d_data(self):
        z_data = np.load(myWin.proj_child.proj_num_path + os.sep + 'Z.npy')
        x_coord = 599 - self.verticalSlider.value()
        np.savetxt(myWin.proj_child.proj_num_path + os.sep + str(x_coord)+'_data.csv',
                    z_data[x_coord], fmt='%.5f')
        msgtext = "数据导出成功，保存在：\n" + myWin.proj_child.proj_num_path.replace('/', '\\') + os.sep + str(x_coord)+' _data.csv'
        msg = QMessageBox.warning(self, "提示", msgtext,
                                    QMessageBox.Ok, QMessageBox.Ok)

    def calc_3d_args(self):
        myWin.progressBar.show()
        self.pushButton_3d.setEnabled(False)
        self.calc_timer.start(150, self)    #启动时钟，每150ms调用timerEvent()


        #线程
        self.args_thread = Argsthread()
        self.args_thread.args_signal.connect(self.args_calback)
        self.args_thread.start()

    def args_calback(self, all_3d_args, x_smr, y_c, y_vvv, y_vmp):
        
        self.step = 100
        myWin.progressBar.setValue(self.step)
        self.pushButton_3d.setEnabled(True)

        self.label_sa.setText(all_3d_args[0])
        self.label_sv.setText(all_3d_args[1])
        self.label_sp.setText(all_3d_args[2])
        self.label_sz.setText(all_3d_args[3])
        self.label_sq.setText(all_3d_args[4])
        self.label_ssk.setText(all_3d_args[5])
        self.label_sku.setText(all_3d_args[6])
        self.label_vvv.setText(all_3d_args[7])
        self.label_vvc.setText(all_3d_args[8])
        self.label_vmp.setText(all_3d_args[9])
        self.label_vmc.setText(all_3d_args[10])
        self.label_sxp.setText(all_3d_args[11])
        self.label_sk.setText(all_3d_args[12])
        self.label_svk.setText(all_3d_args[13])
        self.label_smr.setText(all_3d_args[14])
        self.label_smr1.setText(all_3d_args[15])
        self.label_smr2.setText(all_3d_args[16])
        self.label_sdq.setText(all_3d_args[17])
        self.label_sdr.setText(all_3d_args[18])
        
        #绘制曲线

        self.smrc_fig.axes.cla()
        self.smrc_fig.axes.plot(x_smr, y_c)
        self.smrc_fig.axes.set_xlabel('支承率Smr', fontproperties="SimHei")
        self.smrc_fig.axes.set_ylabel('高度c', fontproperties="SimHei")
        self.smrc_fig.axes.set_title('支承率Smr-高度c曲线图', fontproperties="SimHei")
        self.smrc_fig.draw()

        self.smrvvv_fig.axes.cla()
        self.smrvvv_fig.axes.plot(x_smr, y_vvv)
        self.smrvvv_fig.axes.set_xlabel('支承率Smr', fontproperties="SimHei")
        self.smrvvv_fig.axes.set_ylabel('空体积Vvv', fontproperties="SimHei")
        self.smrvvv_fig.axes.set_title('支承率Smr-空体积Vvv曲线图', fontproperties="SimHei")
        self.smrvvv_fig.draw()

        self.smrvmp_fig.axes.cla()
        self.smrvmp_fig.axes.plot(x_smr, y_vmp)
        self.smrvmp_fig.axes.set_xlabel('支承率Smr', fontproperties="SimHei")
        self.smrvmp_fig.axes.set_ylabel('峰体积Vmp', fontproperties="SimHei")
        self.smrvmp_fig.axes.set_title('支承率Smr-峰体积Vmp曲线图', fontproperties="SimHei")
        self.smrvmp_fig.draw()

        msg = QMessageBox.warning(self, "提示", "计算完成，请查看。",
                                    QMessageBox.Ok, QMessageBox.Ok)

    def timerEvent(self, *args, **kwargs):
        myWin.progressBar.setValue(self.step)
        if self.step >= 100:
            self.calc_timer.stop()
            self.step = 0
            myWin.progressBar.setValue(self.step)
            myWin.progressBar.hide()
            return
        if self.step < 99:
            self.step += 1


    def export_3d_data(self):
        z_data = np.load(myWin.proj_child.proj_num_path + os.sep + 'Z.npy')
        np.savetxt(myWin.proj_child.proj_num_path + os.sep + 'z_data.csv',
                    z_data, fmt='%.5f')
        msgtext = "数据导出成功，保存在：\n" + myWin.proj_child.proj_num_path.replace('/', '\\') + os.sep +'z_data.csv'
        msg = QMessageBox.warning(self, "提示", msgtext,
                                    QMessageBox.Ok, QMessageBox.Ok)



#继承设置窗口类
'''
class MyOptionChildForm(QWidget, Ui_option_form):
    def __init__(self, parent=None):    
        super(MyOptionChildForm, self).__init__(parent)
        self.setupUi(self)
'''


#继承帮助窗口类
class MyHelpChildForm(QWidget, Ui_help_form):
    def __init__(self, parent=None):    
        super(MyHelpChildForm, self).__init__(parent)
        self.setupUi(self)

        #将按钮的点击信号与相关的槽函数进行绑定，点击即触发
        self.pushButton_2d_Rp.clicked.connect(self.show_ra)
        self.pushButton_2d_Rv.clicked.connect(self.show_rv)


    def show_ra(self):
        self.label_2d_title.setText("Rp：最大峰高度<符号：名称>")
        # 开头加<html><head/><body><p>；结束加</p></body></html>
        # 在换行前加</p><p>表示换行
        self.label_2d_content.setText( "<html><head/><body><p>&lt;说明内容，不多于两段，不多于200字&gt;轮廓曲线要素的平均高度表示在基准长度上的轮廓曲线要素的高度平均值。</p><p>所谓轮廓要素，是指将相邻的峰与谷作为一组。在这种情况下，对构成轮廓要素的峰（谷）有最低高度和最低长度的规定，高度（深度）在最大高度 10%以下，或者长度在计算区间长度1%以下的被视作噪声，识别为前后连续的谷（峰）的一部分。谷）有最低高度和最低长度的规定，高度（深度）在最大高度 10%以下，或者长度在计算区间长</p></body></html>")
        self.label_2d_pic.setPixmap(QtGui.QPixmap(":/pic/rp.jpg"))

    def show_rv(self):
        self.label_2d_title.setText("Rv：最大峰高度")
        self.label_2d_content.setText( "<html><head/><body><p>输入说明内容。</p><p>换行继续输入说明内容</p></body></html>")
        self.label_2d_pic.setPixmap(QtGui.QPixmap(":/pic/rp.jpg"))

    def show_rz(self):
        pass

    def show_rc(self):
        pass






#创建一个matplotlib图形绘制类
class MyFigure(FigureCanvas):
    def __init__(self, width=5, height=4, dpi=100, **kw):
        
        #第一步：创建一个创建Figure
        self.fig = Figure(figsize=(width, height), dpi=dpi)
        
        #第二步：在父类中激活Figure窗口
        super(MyFigure,self).__init__(self.fig) #此句必不可少，否则不能显示图形
        #第三步：创建一个子图，用于绘制图形用，111表示子图编号，如matlab的subplot(1,1,1)
        self.axes = self.fig.add_subplot(1, 1, 1)
        
        self.fig.subplots_adjust(bottom=0.2)

        self.axes.grid(True)

        if "xlabel" in kw:
            self.axes.set_xlabel(kw["xlabel"], fontproperties="SimHei")
        if "ylabel" in kw:
            self.axes.set_ylabel(kw["ylabel"], fontproperties="SimHei")
        if "title" in kw:
            self.axes.set_title(kw["title"], fontproperties="SimHei")
            

#继承相机窗口
class MyCameraChildForm(QWidget, Ui_camera_form):
    def __init__(self, parent=None):    
        super(MyCameraChildForm, self).__init__(parent)
        self.setupUi(self)

        self.light_ctrl = Ser("COM7", 9600)
        self.light_ctrl.write("L0".encode())
        
        self.timer_camera = QtCore.QTimer()
        self.cap = cv2.VideoCapture()
        self.CAM_NUM = 0  #打开相机序号，默认为零
        self.LED_SEQ = ["L0", "L1", "L8", "L7", "L6", "L5", "L4", "L3", "L2"]
        
        self.COLOR_FACTOR = np.load("temp/factor.npy")  #相机颜色校正因子

        self.save_image_bool = False    #判断是否拍照的布尔值
        
        

        #连接事件
        self.pushButton_camera_start.clicked.connect(self.pushButton_camera_start_click)
        self.timer_camera.timeout.connect(self.show_camera)
        self.pushButton_camera_save.clicked.connect(self.pushButton_camera_save_click)
        self.pushButton_camera_exit.clicked.connect(self.close)



    #打开相机
    def pushButton_camera_start_click(self):
        if self.timer_camera.isActive() == False:
            flag = self.cap.open(self.CAM_NUM)
            if flag == False:
                msg = QMessageBox.warning(self, "警告", "请检查相机是否正确连接",
                                        QMessageBox.Ok, QMessageBox.Ok)

            else:
                self.timer_camera.start(200)
                self.pushButton_camera_start.setText("结束")

        else:
            self.timer_camera.stop()
            self.cap.release()
            self.label_camera.clear()
            self.pushButton_camera_start.setText("打开")
    
    #实时显示相机的内容
    def show_camera(self):
        #显示图片设置
        flag, self.image = self.cap.read()
        self.image = (self.image * self.COLOR_FACTOR).astype(np.float32)     #色彩校正
        self.image = np.clip(self.image, 0, 255).astype(np.uint8)
        
        show = cv2.resize(self.image, (640, 480))
        show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
        showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0], QtGui.QImage.Format_RGB888)
        
        #保存图片设置
        save_image = cv2.resize(self.image, (2048, 1536))
        save_image = cv2.cvtColor(save_image, cv2.COLOR_BGR2RGB)
        saveImage = QtGui.QImage(save_image.data, save_image.shape[1], save_image.shape[0], QtGui.QImage.Format_RGB888)
        '''
        if self.save_image_bool == True:
            image_name = strftime('%Y%m%d%H%M%S', localtime(time())) + '.jpg'
            saveImage.save(myWin.proj_child.proj_num_path + os.sep + 'images' + os.sep + image_name)
            self.save_image_bool = False
        '''
        #灯依次亮拍照
        if self.save_image_bool == True:
            for light_num in range(0, 9):
                self.light_ctrl.write(self.LED_SEQ[light_num].encode())
                sleep(0.5)

                #显示图片设置
                flag, self.image = self.cap.read()
                ##
                self.image = self.image * self.COLOR_FACTOR
                self.image = np.clip(self.image, 0, 255).astype(np.uint8)

                show = cv2.resize(self.image, (640, 480))
                show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
                
                showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0], QtGui.QImage.Format_RGB888)
                self.label_camera.setPixmap(QtGui.QPixmap.fromImage(showImage))

                sleep(0.5)
                
                #保存图片设置
                save_image = cv2.resize(self.image, (2048, 1536))
                #save_image = cv2.resize(self.image, (640, 480))
                save_image = cv2.cvtColor(save_image, cv2.COLOR_BGR2RGB)
                saveImage = QtGui.QImage(save_image.data, save_image.shape[1], save_image.shape[0], QtGui.QImage.Format_RGB888)
                
                image_name = strftime('%Y%m%d%H%M%S', localtime(time())) + '.jpg'
                saveImage.save(myWin.proj_child.proj_num_path + os.sep + 'images' + os.sep + image_name)
            
            self.light_ctrl.write("L0".encode())
            self.save_image_bool = False
    
        self.label_camera.setPixmap(QtGui.QPixmap.fromImage(showImage))

    #拍照
    def pushButton_camera_save_click(self):
        self.save_image_bool = True
        
    #关闭事件
    def closeEvent(self, event):
        ok = QtWidgets.QPushButton()
        cancel = QtWidgets.QPushButton()
        msg = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning, "退出", "确定退出？ ")
        msg.addButton(ok, QtWidgets.QMessageBox.ActionRole)
        msg.addButton(cancel, QtWidgets.QMessageBox.RejectRole)
        ok.setText("确定")
        cancel.setText("取消")
        if msg.exec_() == QtWidgets.QMessageBox.RejectRole:
            event.ignore()
        else:
            if self.cap.isOpened():
                self.cap.release()
            if self.timer_camera.isActive():
                self.timer_camera.stop()
            event.accept()
    
    #颜色校正
    def correct_color(jpg_name):
        '''
        jpg_name：校正颜色前相机拍摄白纸的图片
        Note：img_corrected = img * factor
        '''
        template = cv2.imread("template.jpg")
        white = cv2.imread(jpg_name)    
        factor = template / white
        return factor

# mayavi场景设置
class Visualization(HasTraits):
    scene = Instance(MlabSceneModel, ())

    '''
    @on_trait_change('scene.activated')
    def update_plot(self):
        # 打开视图即会调用此函数
        # 原因在于@on_trait_change('scene.activated')
        # 在嵌入场景中绘制图形
        self.scene.mlab.test_points3d()
    '''

    
    view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
                     height=250, width=300, show_label=False),
                resizable=True # 随父类窗口大小变化
                )
    
    def plot_surface(self):
        
        Z = np.load(myWin.proj_child.proj_num_path + os.sep + 'Z.npy')
        n=Z.shape[0]
        x, y = np.mgrid[0:n, 0:n]

        mlab.clf()
        surf = self.scene.mlab.surf(x, y, Z, color=(1.0, 1.0, 1.0))
        #surf = self.scene.mlab.surf(x, y, Z, color=(0.6, 0.6, 0.6))
        mlab.axes(xlabel='x轴', ylabel='y轴', zlabel='z轴', extent=[0,599,0,599, np.min(Z)*3, np.max(Z)*3])
        mlab.orientation_axes()
        
        #设置纹理
        texture_path = myWin.proj_child.proj_num_path + os.sep + "texture.jpg"
        img = tvtk.JPEGReader()
        img.file_name = texture_path
        my_texture = tvtk.Texture(input_connection=img.output_port, interpolate=0)
        #纹理映射
        surf.actor.enable_texture = True
        surf.actor.tcoord_generator_mode = 'plane'
        surf.actor.actor.texture = my_texture
        
    def plot_height(self):

        x, y = np.mgrid[0:600, 0:600]
        Z = np.load(myWin.proj_child.proj_num_path + os.sep + 'Z.npy')

        mlab.clf()
        s = self.scene.mlab.surf(x,y,Z)
        mlab.axes(xlabel='x轴', ylabel='y轴', zlabel='z轴', extent=[0,599,0,599, np.min(Z)*3, np.max(Z)*3])
        mlab.orientation_axes()
        #mlab.scalarbar()
        mlab.colorbar()
        



# 定义嵌入场景的窗口
class MayaviQWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        layout = QtWidgets.QVBoxLayout(self)
        layout.setContentsMargins(0,0,0,0)
        layout.setSpacing(0)
        self.visualization = Visualization()

        # The edit_traits call will generate the widget to embed.
        self.ui = self.visualization.edit_traits(parent=self,
                                                 kind='subpanel').control
        layout.addWidget(self.ui)
        self.ui.setParent(self)


# 继承QThread,新建重构计算线程
class Reconthread(QtCore.QThread):

    reconstruct_signal=QtCore.pyqtSignal(bool)

    def __init__(self):
        super(Reconthread, self).__init__()
 
    def run(self):
        try:
            #np.seterr(divide='ignore', invalid='ignore')
            reconstruct_z(myWin.proj_child.proj_num_path)
            self.reconstruct_signal.emit(True)
        except:
            self.reconstruct_signal.emit(False)
        finally:
            self.exit(0)

# 继承QThread,新建三维参数计算线程
class Argsthread(QtCore.QThread):

    args_signal = QtCore.pyqtSignal(tuple, list, list, list, list)
    

    def __init__(self):
        super(Argsthread, self).__init__()
 
    def run(self):

        z_data = np.load(myWin.proj_child.proj_num_path + os.sep + 'Z.npy')
        surface_args_3d = Extract_3d(z_data)
        all_3d_args = surface_args_3d.calc_3d_args()
        #绘制曲线
        z_min, z_max = np.min(z_data), np.max(z_data)
        y_c = list(np.linspace(z_min, z_max))
        x_smr = surface_args_3d.calc_smr(*y_c)
        y_vmp = []
        y_vvv = []
        for i in y_c:
            # 积分法求面积vmp,vvv,vvc,vmc,步长取0.1
            step = 0.1
            x_vmp = list(np.arange(i, z_max, step))  #step=0.1
            x_vvv = list(np.arange(z_min, i, step))
            vmp = np.sum((np.array(surface_args_3d.calc_smr(*x_vmp))) * step)
            vvv = np.sum((1 - (np.array(surface_args_3d.calc_smr(*x_vvv)))) * step)
            y_vmp.append(vmp)
            y_vvv.append(vvv)
        
        self.args_signal.emit(all_3d_args, x_smr, y_c, y_vvv, y_vmp)
        self.exit(0)


        
 


if __name__=="__main__":  
    app = QApplication(sys.argv)  
    myWin = MyMainWindow()  
    myWin.show()  
    sys.exit(app.exec_())  
